package com.rainyun.rcmc.bukkit;

import com.google.common.io.ByteStreams;
import com.rainyun.rc.command.CmdPath;
import com.rainyun.rc.command.CmdUtil;
import com.rainyun.rc.config.NodeConfig;
import com.rainyun.rc.ioc.RainIOC;
import com.rainyun.rc.util.RainF;
import com.rainyun.rc.util.RainFile;
import com.rainyun.rc.util.RainJarTool;
import com.rainyun.rcmc.bukkit.annotation.BeanBukListener;
import com.rainyun.rcmc.bukkit.annotation.Buk;
import com.rainyun.rcmc.bukkit.config.BukYmlNode;
import com.rainyun.rcmc.own.config.RcLang;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.Listener;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;

public class RainBukkitUtil {
	
	/**(常用)获取所有Buk指令(@Cmd+@Buk注解)并绑定指令业务+注册进Bukkit
	 * @param classes 所有相关类
	 * */
	public static Map<String,CmdPath<?>> initAllCmd(List<Class<?>> classes,JavaPlugin plugin) {
		Map<String, CmdPath<?>> cmds = getAllCmds(classes);
		bindAllCmdBiz(classes, cmds);
		regAllCmds(cmds, plugin);
		return cmds;
	}

	/**(常用)获取所有指令(Buk+Cmd注解)*/
	public static Map<String,CmdPath<?>> getAllCmds(List<Class<?>> classes){
		classes = RainF.filter(classes,(o)->o.isAnnotationPresent(Buk.class)
				&&o.getSuperclass().equals(BukCmdPath.class));
		return CmdUtil.getAllCmds(classes);
	}

	/**注册所有指令进Bukkit*/
	public static BukCmds regAllCmds(Map<String,CmdPath<?>> cmdmap, JavaPlugin plugin){
		BukCmds cmds = new BukCmds(plugin);
		cmdmap.forEach((k,v)->{
			cmds.bindCmd(k,(BukCmdPath) v);
		});
		return cmds;
	}
	
	/**自动绑定所有指令业务(Buk+CmdBiz注解)*/
	public static void bindAllCmdBiz(List<Class<?>> classes, Map<String,CmdPath<?>> maincmds){
		classes = RainF.filter(classes,(o)->o.isAnnotationPresent(Buk.class));
		try {
			CmdUtil.initAllCmdBiz(classes, maincmds);
		}catch (Exception e) {
			System.out.println("Failed to bind CmdBiz,check your CmdLine format or something else");
			e.printStackTrace();
		}
	}

	/**(常用)获取并注册所有监听器(BukListener注解)进Bukkit*/
	public static List<Listener> initAllListener(List<Class<?>> classes,JavaPlugin plugin){
		List<Listener> list = getAllListener(classes);
		regAllListener(list,plugin);
		return list;
	}

	/**(常用)获取所有监听器*/
	public static List<Listener> getAllListener(List<Class<?>> classes){
		List<Listener> list = new ArrayList<>();
		classes = RainF.filter(classes,(o)->o.isAnnotationPresent(BeanBukListener.class)
		&&RainF.inArray(o.getInterfaces(),Listener.class));
		for(Class<?> c:classes){
			String en = RainIOC.getIOCKey(c);
			list.add((Listener) RainIOC.get(en));
		}
		return list;
	}

	/**注册事件监听器*/
	public static void regAllListener(List<Listener> list,JavaPlugin plugin) {
		PluginManager pluman = Bukkit.getPluginManager();
		for(Listener lis:list)pluman.registerEvents(lis,plugin);
	}
	/**注册事件监听器*/
	public static void regListener(Listener lis,JavaPlugin plugin) {
		Bukkit.getPluginManager().registerEvents(lis,plugin);
	}
	
	/**通过权限配置树生成符合plugin.yml的格式*/
	public static Map<String,Object> getPermsfromTree(Map<String,Object> tree){
		Map<String,Object> map = new LinkedHashMap<>();
		getPermsfromTree("",tree,map);
		return map;
	}
	protected static void getPermsfromTree(String parentPath,Map<String,Object> tree,Map<String,Object> tm) {
		String parent = parentPath.isEmpty()?parentPath:parentPath+".";
		tree.forEach((k,v)->{
			if(v instanceof Map) {
				Map<String, Object> sm = (Map<String, Object>) v;
				List<String> childs = new ArrayList<String>();
				sm.forEach((k1,v1)->{
					if(v1 instanceof Map)childs.add(parent+k+"."+k1+".*");
					else childs.add(parent+k+"."+k1);
				});
				Map<String, Boolean> childm = new LinkedHashMap<>();
				childs.forEach(o->childm.put(o,true));
				Map<String,Object> newm = new LinkedHashMap<>();
//				newm.put("default","op");
				newm.put("children",childm);
				tm.put(parent+k+".*",newm);
				getPermsfromTree(parent+k,sm,tm);
			}else if(v instanceof List) {
				List<String> sl = (List<String>) v;
				Map<String,Object> newm = new LinkedHashMap<>();
				newm.put("default",sl.get(0));
				sl.remove(0);
				Map<String, Boolean> childm = new LinkedHashMap<>();
				sl.forEach(o->childm.put(o,true));
				newm.put("children",childm);
				tm.put(parent+k,newm);
			}else {
				Map<String,Object> newm = new LinkedHashMap<>();
				newm.put("default",v);
				tm.put(parent+k,newm);
			}
		});
	}

	/**获取当前bukkit的mc版本*/
	public static String getBukVersion(){
		Server ser = Bukkit.getServer();
		String mversion = ser==null?null:
				ser.getClass().getPackage().getName().replace(".", ",").split(",")[3];
		return mversion;
	}

	/**处理bukkit版本为x.y.z格式*/
	public static String formatVer(String bukversion){
		String ver = bukversion;
		ver =ver.replace("v","")
				.replaceAll("_\\D",".");
		return ver;
	}
	/**获取当前bukkit的mc版本(x.y.z格式)*/
	public static String getFormatedBukVer(){
		String ver = getBukVersion();
		if(ver!=null)ver = formatVer(ver);
		return ver;
	}

	/**对jar包进行Nms兼容化
	 * @param jarfile jar文件
	 * @param jarpath jar内特定路径
	 * */
	public static void NmsJarCompatify(File jarfile,String jarpath) throws IOException {
		String version = getBukVersion();
		String objpre = "org/bukkit/craftbukkit/";
		String nmspre = "net/minecraft/server/";
		Map<String[], String> torep = new HashMap<>();
		torep.put(new String[]{objpre,"/"}, objpre + version + "/");
		torep.put(new String[]{nmspre,"/"}, nmspre + version + "/");
		RainJarTool.replaceConstInJar(jarfile,jarpath,torep);
	}

	/**对bukkit的插件文件进行nms兼容化处理
	 * @param files 插件文件(为null即代表所有)*/
	public static void NmsBukCompatify(@Nullable List<File> files){
		Server ser = Bukkit.getServer();
		Logger logger = ser.getLogger();
		String ver = RainBukkitUtil.getBukVersion();
		if(ver!=null){
			String prefix = NodeConfig.getDefaultByEnum(RcLang.prefix).toString();
			prefix = prefix.replace("&","§");
			File root = null;
			try {
				Class<?> cser = Class.forName("org.bukkit.craftbukkit." + ver + ".CraftServer");
				Class<?> mser = Class.forName("net.minecraft.server." + ver + ".MinecraftServer");
				Class<?> ops;
				try{
					ops = Class.forName("org.bukkit.craftbukkit.libs.joptsimple.OptionSet");
				}catch (ClassNotFoundException e){
					ops = Class.forName("joptsimple.OptionSet");
				}

				Method valueOf = ops.getDeclaredMethod("valueOf",String.class);
				Field cf = cser.getDeclaredField("console");
				cf.setAccessible(true);
				Object console = cf.get(ser);
				Field of = mser.getDeclaredField("options");
				Object options = of.get(console);
				File pluginFolder = (File) valueOf.invoke(options,"plugins");
				root = new File(pluginFolder,"rc_tmp_pluinfo");
				root.mkdirs();
				JarFile jar = null;

				if(files==null){//代表全部
					files=new ArrayList<>(Arrays.asList(pluginFolder.listFiles()));
				}
				for(File file:files){
					if(!file.getName().endsWith(".jar"))continue;
					try {
						jar = new JarFile(file);
						JarEntry en = jar.getJarEntry("src/src/main/resources/plugin.yml");
						if(en==null)continue;
						InputStream in = jar.getInputStream(en);
						String constr = new String(ByteStreams.toByteArray(in),StandardCharsets.UTF_8);
						in.close();jar.close();
						Map<String,Object> yml = new Yaml().load(constr);
						if(yml.containsKey("name")&&yml.get("name").equals("RainCore"))continue;
						if(yml.containsKey("nms_current")){
							String cur = (String) yml.get("nms_current");
							List<String> nms = (List<String>) yml.get("nms_class");
							if(!ver.equals(cur)){
								logger.info(prefix+"nms compatifying "+yml.get("name")+" to "+ver);
								for(String jarpath:nms){
									jarpath = jarpath.replace("-","/");
									RainBukkitUtil.NmsJarCompatify(file,jarpath);
								}
								File np = new File(root, "src/src/main/resources/plugin.yml");
								np.createNewFile();
								FileOutputStream os = new FileOutputStream(np,false);
								os.write(constr.getBytes(StandardCharsets.UTF_8));os.close();
								BukYmlNode byn = new BukYmlNode(root, "", null);
								byn.add("plugin","plugin");
								byn.setVerCheck("plugin",false);
								byn.loadAll();
								YamlConfiguration conf = byn.get("plugin");
								conf.set("nms_current",ver);
								conf.set("permissions",yml.get("permissions"));
								byn.saveAll();
								RainJarTool.replaceToJar(file,root, "src/src/main/resources/plugin.yml");
								logger.info(prefix+"nms compatifying success");
							}
						}
					}catch (Exception e){
						e.printStackTrace();
					}finally {
						if(jar!=null)jar.close();
					}
				}
			}catch (IOException e){
				e.printStackTrace();
			}catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} finally {
				if(root!=null) RainFile.delFile(root);
			}
		}
	}

}
